package com.spring;

import com.duolai.bean.BeanDefinition;
import com.spring.annotation.Autowired;
import com.spring.annotation.Component;
import com.spring.annotation.ComponentScan;
import com.spring.annotation.Scope;
import com.spring.interfaces.BeanNameAware;
import com.spring.interfaces.BeanPostProcessor;
import com.spring.interfaces.InitializingBean;

import java.beans.Introspector;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @program: duolai-spring-01
 * @description: 容器上下文下载器
 * @author: chaidl  ！
 * @create: 2021-09-04 13:21
 */
public class DuolaiApplicationContext {
    private Map<String,BeanDefinition> beanDefinitionMap=new ConcurrentHashMap<>(64);
    private final Map<String, Object> singletonObjects = new HashMap<>();
    private Class configClass;
    //后置处理器集合
    private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();
    public DuolaiApplicationContext(Class configClass) {
        //1.先进性属性赋值
        this.configClass=configClass;
        //2.接下来根据配置类对类进行包扫描
        scan(configClass);
        //3.循环创建Bean
        for (Map.Entry<String, BeanDefinition> entity : beanDefinitionMap.entrySet()) {
            //拿到对象姓名
            String beanName = entity.getKey();
            BeanDefinition beanDefinition = entity.getValue();
            //只有是单例的时候才会创建
            if (beanDefinition.getScope().equals("singleton")){
                Object singletonBean = createBean(beanName, beanDefinition);
                singletonObjects.put(beanName,singletonBean);
            }
        }
    }
    //根据配置类进行扫描成为BeanDefinition
    private void scan(Class configClass) {
        //如果配置类配置了ComponentScan注解
        if (configClass.isAnnotationPresent(ComponentScan.class)) {
        //    拿到这个注解配置的包路径
            ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
            String path = componentScanAnnotation.value();
            //要将路径转换为文件路径格式
            path=path.replace(".","/");
            //获取当前的类加载器，当前类肯定是app类加载器
            ClassLoader classLoader = DuolaiApplicationContext.class.getClassLoader();
            URL resource = classLoader.getResource(path);
            //拿到target资源以后将其转换为文件
            File file=new File(resource.getFile());
            if (file.isDirectory()){
                //这会就可以拿到扫描包路径下的所有class文件了
                for (File f : file.listFiles()) {
                    String absolutePath = f.getAbsolutePath();
                    //这会就要拿到userService这个对象，需要截取路径
                    absolutePath=absolutePath.substring(absolutePath.indexOf("com"),absolutePath.indexOf(".class"));
                    absolutePath=absolutePath.replace("/",".");
                    //用类加载器去加载该类
                    try {
                        Class<?> aClass = classLoader.loadClass(absolutePath);
                        //如果当前类有Component注解，则表示要加入容器当中
                        if (aClass.isAnnotationPresent(Component.class)) {
                            //如果当前类实现了BeanPostProcessor，就是进行缓存
                            if (BeanPostProcessor.class.isAssignableFrom(aClass)) {
                            //    通过构造函数强转成BeanPostProcessor
                                BeanPostProcessor instance = (BeanPostProcessor) aClass.getConstructor().newInstance();
                            //    存储到集合中
                                beanPostProcessorList.add(instance);
                            }
                            Component componentAnnotation = aClass.getAnnotation(Component.class);
                            //拿到了要存储的对象名称
                            String beanName = componentAnnotation.value();
                            //如果没有指定对象名称，则使用Spring的方法指定默认名称
                            if ("".equals(beanName)) {
                                beanName = Introspector.decapitalize(aClass.getSimpleName());
                            }
                            BeanDefinition beanDefinition=new BeanDefinition();
                            beanDefinition.setType(aClass);
                            //此处只是判断了当前对象是需要加入到容器当中，但是不清楚它是单列还是多列的
                            //所以我们需要进行判断，如果是原型Bean，则不需要在此创建，在getbean的时候创建
                            if (aClass.isAnnotationPresent(Scope.class)){
                            //    此处还需要判断一下Scope里面的值是prototype还是singleton的
                                Scope scopeAnnotation = aClass.getAnnotation(Scope.class);
                                String scope = scopeAnnotation.value();
                                if ("singleton".equals(scope)){
                                    beanDefinition.setScope("singleton");
                                }else if ("prototype".equals(scope)) {
                                    beanDefinition.setScope("prototype");
                                }
                            }else {
                            //    如果到这里，肯定就是没有这个注解，单列无疑，在此处要创建
                                beanDefinition.setScope("singleton");
                                // 此处应该考虑getBean的时候是通过beanName拿到这个对象的，那么拿到的这个class
                                //我们是不是又要去解析这个class是不是单列的，是不是懒加载的，之前在扫描的时候都已经判断过了，
                                //所以再去判断就显得有点呆，所以Spring引出来一个叫做BeanDefinition的概念
                            //    BeanDefinition里面就会存储当前对象的一些信息
                            }
                            beanDefinitionMap.put(beanName,beanDefinition);
                        }
                    } catch (ClassNotFoundException | NoSuchMethodException e) {
                        e.printStackTrace();
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (InstantiationException e) {
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    /**
     * @Description: 创建对象
     * @Param: [userService]
     * @return: com.duolai.bean.UserService
     * @Author: chaidl  ！
     * @Date: 2021/9/4 14:59
     */
    public Object getBean(String beanName) {
        if (!beanDefinitionMap.containsKey(beanName)){
            throw new NullPointerException("你即将要获取的对象不存在，请检查代码后重试");
        }
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if (beanDefinition.getScope().equals("singleton")){
        //    应该从单例池中获取
            Object singletonBean = singletonObjects.get(beanName);
        //    若果当前单例对象为空，就是继续创建
            if (singletonBean ==null){
                singletonBean = createBean(beanName, beanDefinition);
                singletonObjects.put(beanName,singletonBean);
            }
            return singletonBean;
        }else {
            Object prototype=createBean(beanName,beanDefinition);
            return prototype;
        }
    }
    /**
     * @Description: 创建Bean
     * @Param: [beanName, beanDefinition]
     * @return: java.lang.Object
     * @Author: chaidl  ！
     * @Date: 2021/9/4 15:03
     */
    private Object createBean(String beanName, BeanDefinition beanDefinition) {
        //拿出当前对象的bean定义
        Class type = beanDefinition.getType();
        //默认通过构造函数然后通过反射拿到这个对象
        //默认是无参构造。如果同一个对象有多个有参构造会报错，除非有参构造其中一个加入@autowride
        Object instance=null;
        try {
            instance = type.getConstructor().newInstance();
        // 拿到当前对象的所有属性
            for (Field field : type.getDeclaredFields()) {
                //如果当前属性标识了Autowired注解，就表示要属性注入
                if (field.isAnnotationPresent(Autowired.class)) {
                    //反射必须设置
                    field.setAccessible(true);
                    field.set(instance,getBean(field.getName()));
                }
            }
            //拿到当前对象的名称
            if (instance instanceof BeanNameAware){
                ((BeanNameAware)instance).setBeanName(beanName);
            }
            //初始化前
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                beanPostProcessor.postProcessBeforeInitialization(instance,beanName);
            }
            //初始化任意一个对象实现了InitializingBean接口，都会执行afterPropertiesSet方法
            if (instance instanceof InitializingBean){
                ((InitializingBean) instance).afterPropertiesSet();
            }
            //循环Bean后置处理器集合  初始化后执行
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
              instance= beanPostProcessor.postProcessAfterInitialization(instance,beanName);
            }

        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return instance;
    }
}
